package com.common.dao.abstracts;

import java.util.List;
import java.util.logging.Logger;

import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import javax.persistence.Query;

import com.common.dao.interfaces.IDao;
import com.common.dao.util.Criteria;
import com.common.dao.util.EntityFactory;
import com.common.dao.util.Order;
import com.common.dao.util.QueryBuilder;
import com.common.security.exceptions.DatabaseException;

public abstract class AbstractJPADao<T> implements IDao<T> {

	public void save(T item) throws DatabaseException {
		EntityManager entityManager = EntityFactory.getEntityManager();
		EntityTransaction transaction = entityManager.getTransaction();
		try {
			transaction.begin();
			entityManager.persist(item);
			transaction.commit();
		} catch (Exception e) {
			Logger.getAnonymousLogger().severe("Error " + e.getCause());
			throw new DatabaseException(DatabaseException.DATABASE_EXCEPTION);
		} finally {
			if (transaction.isActive()) {
				transaction.rollback();
			}
			entityManager.close();
		}
	}
	
	public void update(T item) throws DatabaseException {
		EntityManager entityManager = EntityFactory.getEntityManager();
		EntityTransaction transaction = entityManager.getTransaction();
		try {
			transaction.begin();
			entityManager.merge(item);
			transaction.commit();
		} catch (Exception e) {
			Logger.getAnonymousLogger().severe("Error " + e.getCause());
			throw new DatabaseException(DatabaseException.DATABASE_EXCEPTION);
		} finally {
			if (transaction.isActive()) {
				transaction.rollback();
			}
			entityManager.close();
		}
	}
	
	public void delete(T item) throws DatabaseException {
		EntityManager entityManager = EntityFactory.getEntityManager();
		EntityTransaction transaction = entityManager.getTransaction();
		try {
			transaction.begin();
			item = entityManager.find(getEntityClass(), getId(item));
			entityManager.remove(item);
			transaction.commit();
		} catch (Exception e) {
			Logger.getAnonymousLogger().severe("Error " + e.getCause());
			throw new DatabaseException(DatabaseException.DATABASE_EXCEPTION);
		} finally {
			if (transaction.isActive()) {
				transaction.rollback();
			}
			entityManager.close();
		}
	}
	
	public T getById(Long id) throws DatabaseException {
		EntityManager entityManager = EntityFactory.getEntityManager();
		EntityTransaction transaction = entityManager.getTransaction();
		try {
			transaction.begin();
			T item = entityManager.find(getEntityClass(), id);
			transaction.commit();
			return item;
		} catch (Exception e) {
			Logger.getAnonymousLogger().severe("Error " + e.getCause());
			throw new DatabaseException(DatabaseException.DATABASE_EXCEPTION);
		} finally {
			if (transaction.isActive()) {
				transaction.rollback();
			}
			entityManager.close();
		}
	}
	
	@SuppressWarnings("unchecked")
	public List<T> getAll() throws DatabaseException {
		EntityManager entityManager = EntityFactory.getEntityManager();
		EntityTransaction transaction = entityManager.getTransaction();
		Query query = entityManager.createQuery("SELECT FROM " + getEntityClass().getSimpleName());
		try {
			transaction.begin();
			List<T> list = query.getResultList();
			transaction.commit();
			return list;
		} catch (Exception e) {
			Logger.getAnonymousLogger().severe("Error " + e.getCause());
			throw new DatabaseException(DatabaseException.DATABASE_EXCEPTION);
		} finally {
			if (transaction.isActive()) {
				transaction.rollback();
			}
			entityManager.close();
		}
	}
	
	@SuppressWarnings("unchecked")
	public List<T> getByCriteria(List<Criteria> criterias) throws DatabaseException {
		EntityManager entityManager = EntityFactory.getEntityManager();
		EntityTransaction transaction = entityManager.getTransaction();
		String sql = QueryBuilder.createCriteriaQuery(criterias, getEntityClass().getSimpleName());
		Query query = entityManager.createQuery(sql);
		try {
			transaction.begin();
			List<T> list = query.getResultList();
			transaction.commit();
			return list;
		} catch (Exception e) {
			Logger.getAnonymousLogger().severe("Error " + e.getCause());
			throw new DatabaseException(DatabaseException.DATABASE_EXCEPTION);
		} finally {
			if (transaction.isActive()) {
				transaction.rollback();
			}
			entityManager.close();
		}
	}
	
	@SuppressWarnings("unchecked")
	public List<T> getByOrder(List<Order> orders) throws DatabaseException {
		EntityManager entityManager = EntityFactory.getEntityManager();
		EntityTransaction transaction = entityManager.getTransaction();
		String sql = QueryBuilder.createOrderQuery(orders, getEntityClass().getSimpleName());
		Query query = entityManager.createQuery(sql).setMaxResults(1);
		try {
			transaction.begin();
			List<T> list = query.getResultList();
			transaction.commit();
			return list;
		} catch (Exception e) {
			Logger.getAnonymousLogger().severe("Error " + e.getCause());
			throw new DatabaseException(DatabaseException.DATABASE_EXCEPTION);
		} finally {
			if (transaction.isActive()) {
				transaction.rollback();
			}
			entityManager.close();
		}
	}
	
	@SuppressWarnings("unchecked")
	public List<T> getByOrderedCriteria(List<Criteria> criterias, List<Order> orders) throws DatabaseException {
		EntityManager entityManager = EntityFactory.getEntityManager();
		EntityTransaction transaction = entityManager.getTransaction();
		String sql = QueryBuilder.createCriteriaOrderQuery(criterias, orders, getEntityClass().getSimpleName());
		Query query = entityManager.createQuery(sql);
		try {
			transaction.begin();
			List<T> list = query.getResultList();
			transaction.commit();
			return list;
		} catch (Exception e) {
			Logger.getAnonymousLogger().severe("Error " + e.getCause());
			throw new DatabaseException(DatabaseException.DATABASE_EXCEPTION);
		} finally {
			if (transaction.isActive()) {
				transaction.rollback();
			}
			entityManager.close();
		}
	}
	
	protected abstract Class<T> getEntityClass();
	
	protected abstract Long getId(T item);
}
